/* Copyright (C) 
 *
 * Disclaim
 * This program is the property of AI Speech Ltd. It shall be communicated to
 * authorized personnel only. It is not to be disclosed outside the group without
 * prior written consent. If you are not sure if you’re authorized to read this
 * program, please contact info@aispeech.com before reading.
 * 
 */


#include "wtk/utils/wtk_utils.h"

#include <ctype.h>


void wtk_utils_row_dump_arr_s(wtk_array_t* arr, wtk_strbuf_t *buf) {
    if(NULL==buf) {
        return ;
    }

    if(NULL==arr || arr->nslot<1) {
        wtk_strbuf_push_f(buf, "NULL");
        return ;
    }

    wtk_string_t* str;

    int j;
    for(j=0; j<arr->nslot; ++j) {
	str = wtk_utils_array_get_val(arr, j, wtk_string_t*);
	if(NULL!=str && str->len>0) {
	    wtk_strbuf_push_f(buf, "%.*s,", str->len, str->data);
	} else {
	    wtk_strbuf_push_f(buf, "NULL,");
	}
    }

    if(buf->pos>0 && ','==buf->data[buf->pos-1]) {
        --buf->pos;
    }
}



void wtk_utils_row_dump_arr_i(wtk_array_t* arr, wtk_strbuf_t *buf) {
    if(NULL==buf) {
        return ;
    }

    if(NULL==arr || arr->nslot<1) {
        wtk_strbuf_push_f(buf, "NULL");
        return ;
    }

    int j;
    for(j=0; j<arr->nslot; ++j) {
	wtk_strbuf_push_f(buf, "%d,", wtk_utils_array_get_val(arr, j, int));
    }

    if(buf->pos>0 && ','==buf->data[buf->pos-1]) {
        --buf->pos;
    }
}



void wtk_utils_row_dump_arr_f(wtk_array_t* arr, wtk_strbuf_t *buf) {
    if(NULL==buf) {
        return ;
    }

    if(NULL==arr || arr->nslot<1) {
        wtk_strbuf_push_f(buf, "NULL");
        return ;
    }

    int j;
    for(j=0; j<arr->nslot; ++j) {
	wtk_strbuf_push_f(buf, "%f,", wtk_utils_array_get_val(arr, j, int));
    }

    if(buf->pos>0 && ','==buf->data[buf->pos-1]) {
        --buf->pos;
    }
}



void wtk_utils_join_arr_i(wtk_array_t* arr, char sep, wtk_strbuf_t *buf, int reset) {
    if(NULL==arr || arr->nslot<1 || NULL==buf) {
	return ;
    }

    int i;

    if(reset) {
        wtk_strbuf_reset(buf);
    }

    int val;
    int flag = 0;;
    for(i=0; i<arr->nslot; ++i) {
	val = wtk_utils_array_get_val(arr, i, int);
	wtk_strbuf_push_f(buf, "%d", val);

        wtk_strbuf_push_c(buf, sep);
        flag = 1;

	if(buf->pos>4096) {
	    wtk_utils_warn("total length of array are more than 4096, will throw left elements");
	    break;
	}
    }

    if(1==flag) {
        --buf->pos;
    }
}



void wtk_utils_join_arr_f(wtk_array_t* arr, char sep, wtk_strbuf_t *buf, int reset) {
    if(NULL==arr || arr->nslot<1) {
	return ;
    }

    int i;

    if(reset) {
        wtk_strbuf_reset(buf);
    }

    float val;
    int flag = 0;
    for(i=0; i<arr->nslot; ++i) {
	val = wtk_utils_array_get_val(arr, i, float);
	wtk_strbuf_push_f(buf, "%f", val);

        wtk_strbuf_push_c(buf, sep);
        flag = 1;

	if(buf->pos>4096) {
	    wtk_utils_warn("total length of array are more than 4096, will throw left elements");
	    break;
	}
    }

    if(1==flag) {
        --buf->pos;
    }
}





/**
 * @brief sample item with max probability
 */
int wtk_utils_max_sample(wtk_array_t *arr) {
    if(NULL==arr) {
	wtk_utils_error("RAND SAMPLE: sample with an NULL array!");
	return -1;
    } else if(arr->nslot<1) {
	wtk_utils_error("RAND SAMPLE: sample with an empty array!");
	return -1;
    }

    // compute pcf(probability cumulative function)
    int i;
    int idxMax = -1;

    float val;
    float max = 0.0;

    for(i=0; i<arr->nslot;++i) {
	val = wtk_utils_array_get_val(arr, i, float);
	if(val>max) {
	    max = val;
	    idxMax = i;
	}
    }

    return idxMax;
}


/**
 * @brief sample accoring to probability
 */
int wtk_utils_roulette_sample(wtk_array_t *arr) {
    if(NULL==arr) {
	wtk_utils_error("RAND SAMPLE: sample with an NULL array!");
	return -1;
    } else if(arr->nslot<1) {
	wtk_utils_error("RAND SAMPLE: sample with an empty array!");
	return -1;
    }

    float *pcf = (float*)malloc(arr->nslot*sizeof(float));

    // compute pcf(probability cumulative function)
    int i;
    pcf[0] = wtk_utils_array_get_val(arr, 0, float);
    for(i=1; i<arr->nslot;++i) {
	pcf[i] = pcf[i-1] + wtk_utils_array_get_val(arr, i, float);
    }

    float sum = pcf[arr->nslot-1];
    if(sum<FLOATCMP) {
	return -1;
    }

    float rnd = (float)rand()/RAND_MAX;
    for(i=0; i<arr->nslot;++i) {
	if(pcf[i]/sum>=rnd) {
	    break;
	}
    }

    free(pcf);

    if(i<arr->nslot) {
	return i;
    } else {
	return arr->nslot-1;
    }
}


int wtk_utils_randsample(wtk_array_t *arr) {
    return wtk_utils_max_sample(arr);
}



int wtk_utils_string_find_c(const char* str, int len, char c) {
    int i;
    for(i=0; i<len; ++i) {
	if(c==str[i]) {
	    break;
	}
    }

    if(i<len) {
	return i;
    } else {
	return -1;
    }
}



wtk_string_t* wtk_utils_string_sub(wtk_heap_t *heap, char *raw, int rlen, char *pat, int plen, char *new, int nlen) {
    wtk_strbuf_t *buf = wtk_strbuf_new(128, 1.0);
    wtk_strbuf_reset(buf);

    int pos = wtk_str_str(raw, rlen, pat, plen);
    if(-1!=pos) {
	wtk_strbuf_push(buf, raw, pos);
	wtk_strbuf_push(buf, new, nlen);
	wtk_strbuf_push(buf, raw+pos+plen, rlen-pos-plen);
    } else {
	wtk_strbuf_push(buf, raw, rlen);
    }

    wtk_string_t *str = wtk_heap_dup_string(heap, buf->data, buf->pos);
    wtk_strbuf_delete(buf);

    return str;
}



wtk_string_t* wtk_utils_string_gsub(wtk_heap_t *heap, char *raw, int rlen, char *pat, int plen, char *new, int nlen) {
    wtk_strbuf_t *buf = wtk_strbuf_new(128, 1.0);
    wtk_strbuf_reset(buf);

    char *p = raw;
    int len = rlen;

    int pos;
    
    while(len>0) {
	pos = wtk_str_str(p, len, pat, plen);

	if(-1!=pos) {
	    wtk_strbuf_push(buf, p, pos);
	    wtk_strbuf_push(buf, new, nlen);

	    p += (pos+plen);
	    len -= (pos+plen);
	} else {
	    wtk_strbuf_push(buf, p, len);
	    len = -1;
	}
    }

    wtk_string_t *str = wtk_heap_dup_string(heap, buf->data, buf->pos);
    wtk_strbuf_delete(buf);

    return str;
}



/**
 * add item to hash table, is the item exist, overwrite it, otherwise add a new one
 *
 * @param hash hash table
 * @param key key of item
 * @param len length of key
 * @param val value to add
 *
 * @note use this function instead of wtk_hash_add, because the later one make a shallow copy 
 * of key, this function do a deep copy
 */
void wtk_utils_hash_uniq_add(wtk_str_hash_t *hash, char *key, int len, void *val) {
    hash_str_node_t *node;
    wtk_string_t *str = wtk_heap_dup_string(hash->heap, key, len);

    node = wtk_str_hash_find_node(hash, key, len, NULL);
    if(NULL==node) {
	wtk_str_hash_add(hash, str->data, str->len, val);
    } else {
	node->value = val;
    }
}



int wtk_utils_time2min(char *data, int len, wtk_strbuf_t *buf, int reset) {
    if(1==reset) {
        wtk_strbuf_reset(buf);
    }

    int pos = wtk_utils_string_find_c(data, len, ':');
    if(-1==pos) {
        wtk_strbuf_push(buf, data, len);
        return 0;
    } else if(0==pos || len-1==pos) {
        wtk_utils_error("UTILS: [%.*s] is invalid time format", len, data);
        return -1;
    }

    wtk_str_iter_t iter = wtk_char_split_iter(data, len, ':');

    // hour
    wtk_string_t valIter = wtk_str_iter_next(&iter);

    int h = wtk_str_atoi(valIter.data, valIter.len)*60;

    // minutes
    valIter = wtk_str_iter_next(&iter);
    h += wtk_str_atoi(valIter.data, valIter.len);

    wtk_strbuf_push_f(buf, "%d", h);

    return 0;
}



char* wtk_utils_string_to_str(wtk_strbuf_t *buf, char *data, int len) {
    if(NULL==buf || NULL==data || 0==len) {
	return NULL;
    }

    wtk_strbuf_reset(buf);
    wtk_strbuf_push(buf, data, len);
    wtk_strbuf_push_c(buf, 0);

    return buf->data;
}



int wtk_test_utils_string_eq_str(wtk_string_t *str, char *p) {
    int ret;

    if(0==strncmp(str->data, p, str->len)) {
        ret = 1;
    } else {
        printf("expect [%s], but get [%.*s]!\n", p, str->len, str->data);
        ret = 0;
    }

    return ret;
}


/**
 * 设置这个函数的原因：在wtk加载资源的时候，如果在当前节点没有找到，会自动从父节点开始查找（太TMCD了），
 * 如果某些配置项是缺省出现的话，而父节点中有同名配置项的话，这个特性非常坑爹！！！
 * 对于字符串的加载，可以通过wtk_local_cfg_find_string2的最后一个参数来关闭迭代搜索，而对数组，没有提供
 * 类似的接口
 */
wtk_array_t* wtk_utils_local_cfg_find_array_s(wtk_local_cfg_t *lc, char *data, int len) {
    wtk_cfg_item_t *item = wtk_local_cfg_find_local(lc, data, len);
    if(NULL!=item && wtk_cfg_item_is_array(item)) {
        return item->value.array;
    } else {
        return NULL;
    }
}



/**
 * used for debug
 */
void wtk_utils_buf_width_append(wtk_strbuf_t *buf, char *data, int len, int w) {
    if(NULL==buf || w<1) {
        return ;
    }

    if(len<0) {
        len = 0;
    }

    if(w>len) {
        int i;
        for(i=len; i<w; ++i) {
            wtk_strbuf_push_c(buf, ' ');
        }
    }

    if(NULL!=data && len>0) {
        wtk_strbuf_push_f(buf, "%.*s", len, data);
    }
}



void wtk_utils_remove_str_from_arr(wtk_array_t *arr, char *data, int len) {
    if(NULL==arr || NULL==data || len<1) {
        return ;
    }

    int i;
    wtk_string_t *str;

    int flag = 0;
    for(i=0; i<arr->nslot; ++i) {
        str = wtk_utils_array_get_val(arr, i, wtk_string_t*);

        if(wtk_utils_string_equ(str, data, len)) {
            str->data = NULL;
            str->len = 0;

            flag = 1;
        }
    }

    if(1==flag && 1==arr->nslot) {
        wtk_array_reset(arr);
    }
}



int wtk_utils_string_arr_has(wtk_array_t *arr, char *data, int len) {
    if(NULL==arr || NULL==data || len<1) {
        return 0;
    }

    int i;
    wtk_string_t *str;
    for(i=0; i<arr->nslot; ++i) {
        str = wtk_utils_array_get_val(arr, i, wtk_string_t*);
        if(wtk_utils_string_equ(str, data, len)) {
            return 1;
        }
    }

    return 0;
}

int wtk_utils_string_arr_get_idx(wtk_array_t *arr, char *data, int len) {
    if(NULL==arr || NULL==data || len<1) {
	return -1;
    }

    int i;
    wtk_string_t *str;
    for(i=0; i<arr->nslot; ++i) {
	str = wtk_utils_array_get_val(arr, i, wtk_string_t*);
	if(wtk_utils_string_equ(str, data, len)) {
	    return i;
	}
    }

    return -1;
}

wtk_string_t wtk_utils_dir_name(char *data, int len, char sep) {
    wtk_string_t str = {NULL, 0};

    if(NULL==data || len<1) {
        return str;
    }

    char *ptr = data+len-1;

    // remote last seperator
    if(sep==*ptr && len>1) {
        --ptr;
        --len;
    }

    while(ptr>=data && sep!=*ptr) {
        --ptr;
    }

    str.data = data;
    if(ptr>data) {
        str.len = ptr-data;
    } else if(ptr==data && '/'==*ptr){
        str.len = 1;
    } else {
        str.len = len;
    }

    return str;
}



wtk_string_t wtk_utils_base_name(char *data, int len, char sep) {
    wtk_string_t str = {NULL, 0};

    if(NULL==data || len<1) {
        return str;
    }

    char *ptr = data+len-1;
    char *wrdEnd = ptr;

    // remove last seperator
    if(sep==*ptr && len>1) {
        --ptr;
        --wrdEnd;
    }

    while(ptr>=data && sep!=*ptr) {
        --ptr;
    }

    if(ptr==wrdEnd) {
        // only one character
        str.data = ptr;
        str.len = 1;
    } else {
        str.data = ptr+1;
        str.len = wrdEnd-ptr;
    }

    return str;
}


wtk_string_t* wtk_utils_string_array_get(wtk_array_t *arr, char *data, int len) {
    if(NULL==arr || NULL==data || len<1) {
        return NULL;
    }

    int i;
    wtk_string_t *str;
    for(i=0; i<arr->nslot; ++i) {
        str = wtk_utils_array_get_val(arr, i, wtk_string_t*);
        if(wtk_utils_string_equ(str, data, len)) {
            return str;
        }
    }

    return NULL;
}


int wtk_utils_string_array_equ(wtk_array_t *cur, wtk_array_t *ref) {
    if(NULL==cur && NULL==ref) {
        return 1;
    } else if (NULL==cur || NULL==ref) {
        return 0;
    }

    if(cur->nslot != ref->nslot) {
        return 0;
    }

    int i;
    wtk_string_t *str;
    for(i=0; i<cur->nslot; ++i) {
        str = wtk_utils_array_get_val(cur, i, wtk_string_t*);

        if(NULL==wtk_utils_string_array_get_str(ref, str)) {
            return 0;
        }
    }

    return 1;
}



wtk_string_t wtk_utils_string_trim_left(char *data, int len) {
    wtk_string_t str = {NULL, 0};

    if(NULL==data || len<1) {
        return str;
    }

    char *ptr;
    char *dataEnd = data+len;
    for(ptr=data; ptr<dataEnd && isspace(*ptr); ++ptr) {
        ;
    }

    if(ptr!=dataEnd) {
        str.data = ptr;
        str.len = dataEnd-ptr;
    }

    return str;
}


wtk_string_t wtk_utils_string_trim_right(char *data, int len) {
    wtk_string_t str = {NULL, 0};

    if(NULL==data || len<1) {
        return str;
    }

    char *ptr;
    char *dataBeg = data;
    for(ptr=data+len-1; ptr>=dataBeg && isspace(*ptr); --ptr) {
        ;
    }

    if(ptr>=dataBeg) {
        str.data = dataBeg;
        str.len = ptr-dataBeg+1;
    }

    return str;
}



wtk_string_t wtk_utils_string_trim(char *data, int len) {
    wtk_string_t str = wtk_utils_string_trim_left(data, len);
    str = wtk_utils_string_trim_right(str.data, str.len);
    return str;
}



wtk_array_t* wtk_utils_string_split_c(wtk_heap_t *heap, char *data, int len, char c) {
    wtk_array_t *arr = wtk_array_new_h(heap, 10, sizeof(wtk_string_t*));

    wtk_string_t *str;

    int i;
    int cnt;

    char *p;
    char *sp;

    p = data;
    sp = data;
    cnt = 0;
    for(i=0; i<len; ++i,++p) {
	if(*p==c) {
	    if(0!=cnt) {
                str = wtk_heap_dup_string(heap, sp, cnt);
		wtk_array_push2(arr, &str);
	    }
	    cnt = 0;
	    sp = p+1;
	} else {
	    ++cnt;
	}
    }

    if(sp<data+len) {
        str = wtk_heap_dup_string(heap, sp, cnt);
        wtk_array_push2(arr, &str);
    }

    if(arr->nslot<1) {
	return NULL;
    } else {
	return arr;
    }
}



wtk_string_t wtk_utils_string_sub_word(char *data, int len, int start, int end) {
    wtk_string_t str = wtk_utils_string_trim(data, len);

    char *ptrS = NULL;
    char *ptrE = NULL;

    wtk_str_iter_t iter = wtk_char_split_iter(str.data, str.len, ' ');
    wtk_string_t strSub = wtk_str_iter_next(&iter);

    int i;
    for(i=0; strSub.data; ++i, strSub=wtk_str_iter_next(&iter)) {
        if(i==start) {
            ptrS = strSub.data;
        } else if(i==end) {
            ptrE = strSub.data+strSub.len;
        }
    }

    // end bigger than number of word
    if(start<i && end>=i) {
        ptrE = str.data+str.len;
    }

    if(NULL!=ptrS && NULL!=ptrE) {
        str.data = ptrS;
        str.len = ptrE-ptrS;
    } else {
        str.data = NULL;
        str.len = 0;
    }

    return str;
}



void wtk_utils_array_remove(wtk_array_t *arr, int idx) {
    if(NULL==arr || idx<0 || idx>=arr->nslot) {
        return ;
    }

    char* dst = wtk_utils_array_get_addr(arr, idx);
    char* src = wtk_utils_array_get_addr(arr, arr->nslot-1);
    

    //if(dst!=src) {
    //    memcpy(dst, src, arr->slot_size);
    //}

    // modified by smk, del the idx in the order
    while(idx < arr->nslot-1 && dst!=src) {
		dst = wtk_utils_array_get_addr(arr, idx);
		src = wtk_utils_array_get_addr(arr, idx+1);
		memcpy(dst, src, arr->slot_size);  
		idx++;                             
    }  
    --arr->nslot;
}



void wtk_utils_array_shift_remove(wtk_array_t *arr, int idx) {
    if(NULL==arr || idx<0 || idx>=arr->nslot) {
        return ;
    }

    char *dst;
    char *src;

    // shift sequential items
    int i;
    for(i=idx; i<arr->nslot-1; ++i) {
        dst = wtk_utils_array_get_addr(arr, i);
        src = wtk_utils_array_get_addr(arr, i+1);

        memcpy(dst, src, arr->slot_size);
    }

    --arr->nslot;
}



unsigned int wtk_utils_utf8_to_num(char *data, int len) {
    if(!str_is_utf8((const unsigned char*)data, len)) {
        return -1;
    }

    int i;
    int bytes;
    unsigned char *ptr = (unsigned char*)data;
    unsigned int tmp;

    bytes = wtk_utf8_bytes(*ptr);

    tmp = 0;
    for(i=0; i<bytes; ++i) {
        tmp = (tmp<<8)|ptr[i];
    }

    return tmp;
}


void readShortsFromBytes(char *data, int n, unsigned short* buf) {
    int i;
    unsigned char *ptr = (unsigned char*)data;
    for(i=0; i<n; ++i, ptr+=2) {
        buf[i] = *ptr + (*(ptr+1)<<8);
    }
}
